HIVE-29698: Replace ESRI geometry library with JTS in geospatial - #6581
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates Hive’s geospatial (Esri-json / ST_* UDF) implementation to use JTS (Java Topology Suite) as the primary geometry model, while retaining Esri JSON/shape interoperability where required. It updates serialization/deserialization, rewires many UDFs to JTS operations, and adjusts tests/results to match JTS output.
Changes:
- Replace many
com.esri.*geometry usages in UDFs/SerDes withorg.locationtech.jts.*equivalents, including prepared-geometry acceleration for relational predicates. - Introduce JTS-based Esri JSON conversion (
EsriJsonConverter) and adjust Esri JSON factory/feature-class mapping. - Update Maven dependencies (add JTS) and refresh golden test outputs for geospatial UDFs.
Reviewed changes
Copilot reviewed 111 out of 111 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| serde/src/java/org/apache/hadoop/hive/serde2/esriJson/serializer/SpatialReferenceJsonSerializer.java | Serialize WKID as JSON spatialReference object for JTS-based model. |
| serde/src/java/org/apache/hadoop/hive/serde2/esriJson/serializer/GeometryTypeJsonSerializer.java | Serialize Esri geometryType as a canonical esriGeometry* string. |
| serde/src/java/org/apache/hadoop/hive/serde2/esriJson/serializer/GeometryJsonSerializer.java | Serialize JTS Geometry to Esri JSON via WKB round-trip through Esri engine. |
| serde/src/java/org/apache/hadoop/hive/serde2/esriJson/deserializer/SpatialReferenceJsonDeserializer.java | Parse spatialReference object into integer WKID. |
| serde/src/java/org/apache/hadoop/hive/serde2/esriJson/deserializer/GeometryTypeJsonDeserializer.java | Parse geometryType JSON string into canonical esriGeometry* string. |
| serde/src/java/org/apache/hadoop/hive/serde2/esriJson/deserializer/GeometryJsonDeserializer.java | Parse Esri JSON to JTS Geometry via Esri parser + WKB conversion. |
| serde/pom.xml | Add jts-core dependency for serde module. |
| ql/src/test/results/clientpositive/llap/geospatial_udfs.q.out | Update expected outputs due to JTS numeric formatting / geometry differences. |
| ql/src/test/queries/clientpositive/geospatial_udfs.q | Add output normalization for floating-point stability across JDKs. |
| ql/src/test/org/apache/hadoop/hive/ql/udf/esri/TestStGeomFromShape.java | Update tests to assert JTS geometry properties/SRID instead of Esri geometry equality. |
| ql/src/test/org/apache/hadoop/hive/ql/udf/esri/TestStCentroid.java | Update centroid assertions to compare numeric coordinates rather than Esri Point. |
| ql/src/test/org/apache/hadoop/hive/ql/udf/esri/serde/TestGeoJsonSerDe.java | Update SerDe tests to construct expected geometries using JTS. |
| ql/src/test/org/apache/hadoop/hive/ql/udf/esri/serde/TestEsriJsonSerDe.java | Update Esri JSON SerDe tests to use JTS geometry creation. |
| ql/src/test/org/apache/hadoop/hive/ql/udf/esri/serde/JsonSerDeTestingBase.java | Update SerDe test helpers to validate JTS geometry outputs. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Z.java | Switch Z-accessor to JTS coordinate Z handling. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Y.java | Switch Y-accessor to JTS point Y. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_X.java | Switch X-accessor to JTS point X. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Within.java | Implement within using JTS (and prepared geometry path). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Union.java | Implement union using JTS UnaryUnionOp. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Touches.java | Implement touches using JTS (and prepared geometry path). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_SymmetricDiff.java | Implement symmetric difference using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_StartPoint.java | Implement start point extraction using JTS LineString. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Relate.java | Implement DE-9IM relate using JTS relate. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Polygon.java | Construct/parse polygon via JTS WKT reader and type checks. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_PolyFromWKB.java | Parse polygon from WKB using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_PointZ.java | Construct Z/M point using JTS coordinate variants. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_PointN.java | Extract Nth point from line/multipoint using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_PointFromWKB.java | Parse point from WKB using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Point.java | Construct/parse point using JTS coordinates and WKT reader. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Overlaps.java | Implement overlaps using JTS (and prepared geometry path). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_NumPoints.java | Use JTS getNumPoints(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_NumInteriorRing.java | Use JTS polygon interior ring count. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_NumGeometries.java | Use JTS getNumGeometries() for multi-geometries. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MultiPolygon.java | Parse MultiPolygon via JTS WKT reader. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MultiPoint.java | Construct/parse MultiPoint via JTS geometry factory / WKT reader. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MultiLineString.java | Construct/parse MultiLineString via JTS geometry factory / WKT reader. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MPolyFromWKB.java | Parse multi-polygon from WKB using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MPointFromWKB.java | Parse multi-point from WKB using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MLineFromWKB.java | Parse multi-line from WKB using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MinZ.java | Compute min Z by iterating coordinates in JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MinY.java | Use JTS envelope minY; handle empty geometry. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MinX.java | Use JTS envelope minX; handle empty geometry. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MinM.java | Compute min M by iterating coordinates in JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MaxZ.java | Compute max Z by iterating coordinates in JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MaxY.java | Use JTS envelope maxY; handle empty geometry. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MaxX.java | Use JTS envelope maxX; handle empty geometry. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_MaxM.java | Compute max M by iterating coordinates in JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_M.java | Read M ordinate from JTS point coordinate. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_LineString.java | Construct/parse LineString using JTS geometry factory / WKT reader. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_LineFromWKB.java | Parse LineString from WKB using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Length.java | Use JTS getLength(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_IsSimple.java | Use JTS isSimple(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_IsRing.java | Implement ring check using JTS LineString.isClosed/isSimple. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_IsMeasured.java | Implement measured check using JTS coordinate ordinates. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_IsEmpty.java | Use JTS isEmpty(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_IsClosed.java | Implement closed check for LineString/MultiLineString using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Is3D.java | Implement 3D check using JTS coordinate ordinates. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Intersects.java | Implement intersects using JTS (and prepared geometry path). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Intersection.java | Implement intersection using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_InteriorRingN.java | Extract polygon hole ring using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeomFromWKB.java | Parse arbitrary geometry from WKB using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeomFromText.java | Parse WKT into JTS geometry and set SRID. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeomFromShape.java | Convert Esri shape bytes to JTS geometry via converter. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeomFromJson.java | Parse Esri JSON to JTS via converter and add cleanup on close. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeomFromGeoJson.java | Parse GeoJSON to JTS via GeoJSON reader and add cleanup on close. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeometryRelational.java | Replace Esri acceleration with JTS PreparedGeometry. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeometryN.java | Implement geometryN extraction via JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeomCollection.java | Parse geometry collections via JTS WKT reader. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_GeodesicLengthWGS84.java | Reimplement geodesic length using JTS coordinate sequences + Haversine. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_ExteriorRing.java | Return exterior ring as LineString using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Equals.java | Implement topological equality via JTS equalsTopo. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_EnvIntersects.java | Implement envelope intersects via JTS envelopes. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Envelope.java | Return envelope geometry using JTS getEnvelope(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_EndPoint.java | Implement end point extraction using JTS LineString. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_DistanceSphere.java | Compute great-circle distance using JTS points + Haversine. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Distance.java | Implement distance using JTS distance(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Disjoint.java | Implement disjoint using JTS (and prepared geometry path). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Dimension.java | Use JTS getDimension(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Difference.java | Implement difference using JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Crosses.java | Implement crosses using JTS (and prepared geometry path). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_CoordDim.java | Infer coordinate dimension using JTS coordinate Z/M. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_ConvexHull.java | Implement convex hull using JTS (with GeometryCollection fallback). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Contains.java | Implement contains using JTS (and prepared geometry path). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Centroid.java | Implement centroid using JTS getCentroid(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Buffer.java | Implement buffer using JTS buffer(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Boundary.java | Implement boundary using JTS and normalize LinearRing to LineString. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_BinEnvelope.java | Use JTS Envelope and return polygon geometry for bins. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Bin.java | Use JTS Point to compute bin IDs. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_AsText.java | Output WKT via JTS writers and normalize dimension spacing. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_AsShape.java | Convert JTS geometry to Esri shape bytes via converter. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_AsJson.java | Convert JTS geometry to Esri JSON via converter. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_AsGeoJson.java | Output GeoJSON using JTS GeoJSON writer. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_AsBinary.java | Output WKB using dimension-aware JTS writer. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Area.java | Use JTS getArea(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Aggr_Union.java | Aggregate union using JTS UnaryUnionOp. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Aggr_Intersection.java | Aggregate intersection using iterative JTS intersections. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/ST_Aggr_ConvexHull.java | Aggregate convex hull using JTS GeometryCollection.convexHull(). |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/serde/GeoJsonSerDe.java | Update GeoJSON SerDe to use JTS readers/writers. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/serde/EsriJsonSerDe.java | Update Esri JSON SerDe to use EsriJsonConverter for parsing/serialization. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/serde/BaseJsonSerDe.java | Generalize SerDe base from OGCGeometry to JTS Geometry. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/HiveGeometryOIHelper.java | Update OI helper to return JTS Geometry/Point and parse WKT via JTS. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/Haversine.java | Switch distance helper from Esri OGCPoint to JTS Point. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/EsriJsonConverter.java | Add standalone JTS <-> Esri JSON conversion without ESRI geometry library dependency. |
| ql/src/java/org/apache/hadoop/hive/ql/udf/esri/BinUtils.java | Switch bin envelope type to JTS Envelope and adjust query methods to return envelopes. |
| ql/src/java/org/apache/hadoop/hive/ql/io/esriJson/EsriJsonFactory.java | Register only Geometry (JTS) serializers/deserializers; move other mappings to field annotations. |
| ql/src/java/org/apache/hadoop/hive/ql/io/esriJson/EsriFeatureClass.java | Update feature-class schema to use String geometryType + int WKID with field-level (de)serializers. |
| ql/src/java/org/apache/hadoop/hive/ql/io/esriJson/EsriFeature.java | Switch feature geometry field to JTS Geometry. |
| ql/pom.xml | Replace ESRI dependency with JTS core + io-common in ql module and shade includes. |
| pom.xml | Add jts.version property. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| /** | ||
| * Golden bytes hand-encoded from the ESRI Shapefile spec (little-endian), so this checks | ||
| * the reader against the on-disk format itself rather than only against our own writer. |
There was a problem hiding this comment.
Note about spec vs our writer: your current code may not handle some cases that are ok according to the the spec. An example I know are (multi)polygons - the spec does not mandate any ordering among external / internal rings, while your reader relies on hulls being followed by their holes. AFAIK the old lib will also keep this convention, so it is ok to assume it, but based on the lib and not the spec.
|
|
||
| /** | ||
| * Computes the geodesic length of a single LineString by summing Haversine | ||
| * distances between consecutive vertices. Iterates the CoordinateSequence |
There was a problem hiding this comment.
I don't think that it is ok to call this ST_GeodesicLengthWGS84 with Haversine formula as geodesic + WGS84 implies ellipsoid. AFAIK the Esri lib uses Vincenty underneath, which is not included in JTS.
I would
a: rename this function to ST_LengthSphere (redshift has this: https://docs.aws.amazon.com/redshift/latest/dg/ST_LengthSphere-function.html)
b: keep the ESRI geometry library to fill this gap in JTS until a replacement lib is found for ellipsoid computations
Note that the Esri implementation also sums the segment length in more sophisticated way to reduce precision loss when adding doubles. jts also has tools for this: https://github.com/locationtech/jts/blob/master/modules/core/src/main/java/org/locationtech/jts/math/DD.java
Probably this is not that important, but can be another source of small differences.
A bigger issue IMO is that this function is not tested at all. Impala has a few tests generated from the original spatail framwork for Hadoop tests: https://github.com/apache/impala/blob/780e6683a21dae3622744a82f92e155ae06e13f2/testdata/workloads/functional-query/queries/QueryTest/geospatial-esri.test#L397
| } | ||
|
|
||
| @Test | ||
| public void testMultiPolygonRoundTrip() throws Exception { |
There was a problem hiding this comment.
It would be nice to add multipolygon with holes, as it the most complex case to (de)serialize due to ring orientation rules.
| Geometry back = roundTrip(text); | ||
| assertTrue("Expected a MultiPolygon!", back instanceof MultiPolygon); | ||
| assertEquals(2, back.getNumGeometries()); | ||
| assertTrue(back.equalsTopo(wkt(text))); |
There was a problem hiding this comment.
Is there a reason for using equalsTopo? It can hide subtle differences like reordering rings. Not sure if it enforces ring orientation.
IMO equalsExact should be also true here and could catch more bugs. It has a version with tolerance to accept small rounding of coordinates:
https://locationtech.github.io/jts/javadoc/org/locationtech/jts/geom/Geometry.html#equalsExact-org.locationtech.jts.geom.Geometry-double-
| -- Truncate floating-point numbers to 12 decimal places for cross-platform reproducibility | ||
| -- (JTS buffer uses Math.cos/sin which can differ by 1 ULP across JDK implementations) | ||
| --! qt:replace:/(\d+\.\d{12})\d+/$1/ | ||
| -- create a table with two columns one for each point. |
There was a problem hiding this comment.
I recently created HIVE-29726 about a bug in st_intersects() on multilinestrings. The bug comes from the ESRI lib, so this patch should fix it. Do you think that it makes sense to create a PR for that before this change is merged, or lets fix the issue with that?
Anyway, it would be nice to have a regression test for it, so that the following query returns false:
select st_intersects(st_geomfromtext('LINESTRING(1 0, 1 1)'), st_geomfromtext('MULTILINESTRING((0 0, 0 1), (2 0, 2 1))'));
There was a problem hiding this comment.
I won't pull in that change in this, but we can independently have a test, if you already have one as a separate PR after this, to increase the test coverage
|
|
||
| OGCGeometry bufferedGeometry = ogcGeometry.buffer(distance.get()); | ||
| // TODO persist type information (polygon vs multipolygon) | ||
| Geometry bufferedGeometry = geom.buffer(distance.get(), 24); |
There was a problem hiding this comment.
24 quadrantSegments is chosen to match Esri lib behavior? A comment could mention this.
(this change is familiar to me from Trino PR description: trinodb/trino#27881)
| @@ -17,14 +17,13 @@ | |||
| */ | |||
| package org.apache.hadoop.hive.ql.udf.esri; | |||
There was a problem hiding this comment.
Do you plan to rename the package? At this point there is not much Esri about these functions anymore. Probably best done in another commit to reduce noise.
There was a problem hiding this comment.
I don't plan to do this as part of this PR, considering compat, what are you folks doing? In case you had any packages with esri or so?
There was a problem hiding this comment.
Impala's handling of this changed recently, I copied the whole dir from Hive to Impala to be able to make modifications:
apache/impala@d51c3c1
So from Impala's point of view Hive is free to modify / rename / delete these functions.
My plan is to keep the original ESRI lib around for a while to provide compatibility for old workloads, and deprecate it at same point when the new lib seems stable.
|
|
||
| public static final int WKID_UNKNOWN = 0; | ||
|
|
||
| // Magic byte at offset 4 to distinguish new WKB format from old ESRI format. |
There was a problem hiding this comment.
I don't have a better solution, but I am worried about interoperability. If there are actually tables with the old format, and users insert to those after this change, then we'll have mixed tables. Reading those will be really painful with any engine other than Hive.
Is there are function that just reads a BINARY and writes it out in the the new format? Would be useful to rewrite tables to get rid of the old format. ST_GeomFromShape does only read old format, right?
In Impala I made the choice of Esri shape vs new format (WKB) a startup flag in https://gerrit.cloudera.org/#/c/24549/ . This is less convenient than accepting both, but I see it as a temporary solution before adding GEOMETRY type and I don't want to commit to a custom format for that time.
What is the plan in Hive about adding GEOMETRY type? That would be an unavoidable breaking change, as functions that return BINARY shapes would need to return geometry (e.g. st_point), which would break queries that relied on the type being BINARY. Adding this new format now would mean that you'll need to be able to read it later, e.g by having ST_GeomFromFromNewHiveShape() (or accepting both formats in ST_GeomFromShape() ).
| * @return a JTS Geometry, or {@code null} for the Null/Unknown shape type | ||
| * @throws IllegalArgumentException if the buffer contains an unsupported or malformed shape | ||
| */ | ||
| public static Geometry fromEsriShape(ByteBuffer shapeBuffer) { |
There was a problem hiding this comment.
It is a bit inconsistent that this function expects the shape file format part of a shape, while geometryFromEsriShape expects the header. Could be renamed, e.g. to fromEsriShapeBody.
| return GeometryUtils.geometryToEsriShapeBytesWritable(geometry, wkid, OGCType.UNKNOWN); | ||
| ByteBuffer shapeBuffer = ByteBuffer.wrap(shape.getBytes(), 0, shape.getLength()) | ||
| .order(ByteOrder.LITTLE_ENDIAN); | ||
| Geometry jtsGeom = EsriShapeConverter.fromEsriShape(shapeBuffer); |
There was a problem hiding this comment.
This function reads only the old format, right? Can you add a comment about that?
| /** | ||
| * Ellipsoidal geodesic distance between two lon/lat points on the WGS84 spheroid, | ||
| * using Vincenty's inverse formula (the same ellipsoidal model the ESRI library used, | ||
| * so results match the previous implementation). Coordinates are in degrees, result |
There was a problem hiding this comment.
Wouldn't it be simpler to keep to ESRI lib for this functionality? https://github.com/Esri/geometry-api-java/blob/master/src/main/java/com/esri/core/geometry/GeoDist.java has its own Vincenty, or the same function could be used an in the original UDF.
There was a problem hiding this comment.
Reverted the reimplementation. ST_GeodesicLengthWGS84 delegates to GeometryEngine.geodesicDistanceOnWGS84.
| private static void assertEqualsNormalized(Geometry expected, Geometry actual) { | ||
| Geometry e = expected.copy(); | ||
| Geometry a = actual.copy(); | ||
| e.normalize(); |
There was a problem hiding this comment.
Is normalization really needed? The round trips should not change the order of coordinates. Also, fixing orientation can actually hide bugs.
I still think that equalsExact with tolerance is the right way to check round trips. The order of coordinates can matter, e.g. when representing a route with a linestring reversing it change semantics.
There was a problem hiding this comment.
Dropped normalization. Round-trip checks now use equalsExact(expected, actual, EPSILON) directly, so coordinate order and ring orientation must be preserved.
| -- Truncate floating-point numbers to 12 decimal places for cross-platform reproducibility | ||
| -- (JTS buffer uses Math.cos/sin which can differ by 1 ULP across JDK implementations) | ||
| --! qt:replace:/(\d+\.\d{12})\d+/$1/ | ||
| -- create a table with two columns one for each point. |
| private static final int TYPE_POLYGON_M = 25; | ||
| private static final int TYPE_MULTIPOINT_M = 28; | ||
|
|
||
| // ESRI marks an absent measure with any value less than this sentinel. |
There was a problem hiding this comment.
The code uses <=
Which one is correct? Based on googling it seems that ==-1e38 is also considered no data.
There was a problem hiding this comment.
<= is correct on read — kept it. Exactly -1e38 is the no-data marker written by ArcView/GDAL and others, and a real measure is never exactly -1e38, so <= catches that marker as well as any value below it. The only actual bug was on the write side: absent M was written as -1e38, which a strict-< reader (e.g. ESRI's own Interop.translateFromAVNaN) would read back as a valid measure. Changed the write value to -Double.MAX_VALUE (matching ESRI's translateToAVNaN), which every reader interprets as no-data.
|
csringhofer
left a comment
There was a problem hiding this comment.
The PR's text no longer matches what the change does. Otherwise looks good to me.
abstractdog
left a comment
There was a problem hiding this comment.
+1
thanks @csringhofer, for chiming in with geospatial background, my high-level review was not accurate, I wish we could always collaborate with other engines in those topics that sometimes has only a single person per engine :)
and thanks @ayushtkn for addressing all the comments
|
Thanx @abstractdog and @csringhofer for the reviews!!! |



What changes were proposed in this pull request?
Migrate the geospatial plugin from ESRI geometry-api-java to JTS (Java Topology Suite) as the core geometry library.
Why are the changes needed?
JTS is more widely used, better maintained, and provides the foundation for upcoming Iceberg geometry type support.
Key design decisions:
Does this PR introduce any user-facing change?
Minimal, and best-effort compatible:
How was this patch tested?
CI & Manually on docker